//Nirma Studio, 2013
//Файл функций всех объектов игры

using namespace std;

class Object {
	public:
		int 		cordX, 		cordY, 			distance;
		int 		level,  	crit_chance, 	dodge_chance,   max_level;
		int 		move_speed, attack_range, 	sight_range, 	cost[3];
		string 		name, 		armor_type, 	attack_type[2], race;
		float 		attack, 	health, 		mana, 	 		attack_speed;
		float 		hp_regen, 	mana_regen, 	experience, 	exp_amount;
		bool 		in_sight, 	have_target, 	horrified;
		bool 		dead,		x,				y;

		int 		max_sight_range,	max_attack_range, 	max_move_speed;
		float 		max_experience,		max_attack,			max_hp_regen;
		float 		max_mana_regen,		max_health,			max_mana;

		Object();
		~Object();
		void 	stat(); 			// вывод полной статистики объекта
		void 	ViewHp(); 			// вывод состояния здоровья объекта
		float 	get_exp_amount(float hp, float mp, float dmg); // получение индекса опыта объекта
		void 	gain_exp(int x); 	// передача опыта объекту
		void 	gain_level(); 		// повышение уровня объекта
		void 	take_dmg(float dmg, string attack, int crit); 		// получение объектом урона
		bool 	DeadOrNot(bool dead_index, int exp_amount,Object *target); 	// проверка на смертность цели объекта
		void 	fight(Object *target1, Object *target2); 			// цикл боя
		int 	RangeDetect(int cordX1, int cordY1, int cordX2, int cordY2); // определение дистанции до цели
		bool 	TargetInSight(int distance, int sight_distance); 	// определение является ли цель видимой
		bool 	TargetIsEnemy(string race1, string race2); 			// является ли цель врагом
		bool 	TargetInAttackRange(int distance, int attack_distance); // определение является ли цедь в зоне поражения
		void 	buf(Object *target, int arg, string type, float amount); // баф или дебаф на объект
};

Object::Object(){}

Object::~Object(){}

void Object::stat(){
	cout << endl;
	cout << "Name:      	" 	<< setw(10) << name                   << endl;
	cout << "Level:     	" 	<< setw(10) << level                  << endl;
	cout << "Health:    	" 	<< setw(10) << float(health)          << endl;
	cout << "Mana:      	" 	<< setw(10) << float(mana)            << endl;
	cout << "Attack:    	" 	<< setw(10) << float(attack)          << endl;
	cout << "Attack Type:   "  	<< setw(11) << attack_type[0]		  << endl;
	cout << "Armor Type:    "	<< setw(11) << armor_type[0]		  << endl;
	cout << "Crit Chance:   "  	<< setw(11) << crit_chance 			  << endl;
	cout << "Dodge Chance:  "  	<< setw(11) << dodge_chance 		  << endl;
	cout << "Move Speed: 	"	<< setw(10) << move_speed 			  << endl;
	cout << "Attack Range:  "	<< setw(11) << attack_range  		  << endl;
	cout << "Sight Range:   "	<< setw(11) << sight_range 			  << endl;
	cout << "HP Regen:      "	<< setw(11) << hp_regen 			  << endl;
	cout << "Mana Regen:    "	<< setw(11) << mana_regen 			  << endl;
	cout << "Experience:    " 	<< setw(11) << float(experience)      << endl;
	cout << "Max Experience:" 	<< setw(11) << float(max_experience)  << endl;
	cout << "Coords         "   << setw(10) << cordX << " / "<< cordY << endl;
	cout << "Cost:          "	<< setw(2)  << cost[0] << " / " << cost[1] << " / " << cost[2] << endl;
	cout << endl;
}

void Object::ViewHp(){
		cout << "______  Unit " <<  name << " have " <<
			float(health) << " health" << endl;
}

float Object::get_exp_amount(float hp, float mp, float dmg){
	float x = float((hp + (mp * 3) + (dmg * 5))/7);
	return x;
}

void Object::gain_exp(int x){
	experience += x;
	if(experience >= max_experience) Object::gain_level();
}

void Object::gain_level(){
	experience 		    = 0;
	level 			    += 1;
	attack     		    = attack 		   * 1.2;
	health     		    = health 		   * 1.3;
	mana       		    = mana 	 		   * 1.2;
	max_experience      = max_experience   * 1.4;
	move_speed 		    = move_speed 	   * 1.1;
	sight_range 	    = sight_range 	   * 1.1;
	attack_range 	    = attack_range 	   * 1.1;
	max_move_speed 		= max_move_speed   * 1.1;
	max_attack_range 	= max_attack_range * 1.1;
	max_sight_range 	= max_sight_range  * 1.1;
	max_health 			= max_health 	   * 1.3;
	max_mana 			= max_mana 	       * 1.2;
	max_attack 			= max_attack       * 1.2;
}

void Object::take_dmg(float dmg, string attack, int crit){
	bool critical = false; 		// показатель критического удара
	if(attack == armor_type) 			dmg *= 1.25; // проверка на уязвимости типа брони против типа атаки
	if((rand() % 100 + 1) <= crit){
		dmg *= 2; 				// если крит, то увеличиваем уров вдвое
		critical = true; 		// и показатель крита ставим
	}
	if((rand() % 100 + 1) > dodge_chance){ 		// если не уклонился
		health -= dmg;
		if(critical)	cout << "______  " << name << "("  // если крит
			<< health << ")" << " taken CRITICLAL damage = " << float(dmg) << endl;
		else cout << "______  " << name << "(" << health << ")" // если не крит
			<< " taken damage = " << float(dmg) << endl;
		if(health <= 0) dead = true; 			// если хп меньше или равно нулю, то показатель смерти = true
	}
	else cout << "______  " << name << " EVOID attack" << endl; // если уклоненился
}

bool Object::DeadOrNot(bool target_dead, int exp_amount, Object *target){
	if(target_dead == true){ 	// если цель мертва
		gain_exp(exp_amount); 	// получаем опыт за уничтожение цели
		delete target; 			// удаляем убитую цель
		return true; 			// возвращаем подтверждение ликвидации цели
	}
	else return false;
}

void Object::fight(Object *target1, Object *target2){
	while(1){ 		// бесконечный цикл
		if(TargetIsEnemy(target1->race, target2->race)){ // если расы объектов различны
			x = target1->DeadOrNot(target2->dead, target2->exp_amount, target2);
			y = target2->DeadOrNot(target1->dead, target1->exp_amount, target1);
			if(x){ 			// если первый мёртв то пишем кто победим и выходим из боя
				cout << endl;
				cout << target1->name << "(" << target1->health << ")" << " WON" << endl;
				cout << endl;
				break;
			}
			else if(y){		// если второй мёртв то пишем кто победим и выходим из боя
					cout << endl;
					cout << target2->name << "(" << target2->health << ")" << " WON" << endl;
					cout << endl;
					break;
				}
				else{ 		// если оба живы, то продолжаем бой
					// если первый объект в зоне поражения второго, то наносим ему урон
					if(TargetInAttackRange(RangeDetect(target1->cordX, target1->cordY,
						 target2->cordX, target2->cordY), target2->attack_range)){
					cout << target2->name << " have a enemy in attack range. " << endl;
					target1->take_dmg(target2->attack, target2->attack_type[1], target2->crit_chance);}
					// если второй объект в зоне поражения первого, то наносим ему урон
					if(TargetInAttackRange(RangeDetect(target1->cordX, target1->cordY,
						 target2->cordX, target2->cordY), target1->attack_range)){
					cout << target1->name << " have a enemy in attack range. " << endl;
					target2->take_dmg(target1->attack, target1->attack_type[1], target1->crit_chance);}
				}
			}
		}
}

int Object::RangeDetect(int cordX1, int cordY1, int cordX2, int cordY2){
 	return sqrt(pow(abs(cordX1 - cordX2), 2) + pow(abs(cordY1 - cordY2), 2));
}

bool Object::TargetInSight(int distance, int sight_distance){
	if(distance <= sight_distance) return true;
	return false;
}

bool Object::TargetIsEnemy(string race1, string race2){
	if(race1 != race2) return true;
	return false;
}

bool Object::TargetInAttackRange(int distance, int attack_distance){
	if(distance <= attack_distance) return true;
	return false;
}

void Object::buf(Object *target, int arg, string type, float amount){
	if(type == "+"){
		switch(arg){	
			case 1:{
			    target->health += amount;
			    if(target->health < 0) target->health = 0;
			}
			case 2:{
			    target->mana += amount;
			    if(target->mana < 0) target->mana = 0;
			}
			case 3:{
				target->attack += amount;
			    if(target->attack < 0) target->attack = 0;
			}
			case 4:{
			    target->move_speed += amount;
			    if(target->move_speed < 0) target->move_speed = 0;
			}
			case 5:{
			    target->attack_range += amount;
			    if(target->attack_range < 0) target->attack_range = 0;
			}
			case 6:{
			    target->sight_range += amount;
			    if(target->sight_range < 0) target->sight_range = 0;
			}
		}
	}
	else if(type == "*"){
		switch(arg){	
			case 1:         target->health 			*= amount;
			case 2:			target->mana 			*= amount;
			case 3:			target->attack 			*= amount;
			case 4:		    target->move_speed 		*= amount;
			case 5: 	    target->attack_range 	*= amount;
			case 6: 	    target->sight_range 	*= amount;
		}
	}
	else cout << "Bad buf type" << endl;
}